﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace EnumerableExtensions
{
    class Program
    {
        static void Main(string[] args)
        {
            var ints = new int[] { 3, 6, 8, 3, 5, 5, 16, 9, 2, 5 };
            var sorted = ints.OrderBy(c => c);
            int median = ints.Median();
            var frequencies = ints.Frequencies();
            int mood = ints.Mood();
            var ranks = ints.Rank(s => s, (s, r) => new { s, r }).ToList();
        }
    }

    public static class IEnumExtensions
    {
        public static int Median(this IEnumerable<int> source)
        {
            var ordered = source.OrderBy(c => c).ToList();
            int count = source.Count();
            int halfcount = count / 2;
            int result = count % 2 != 0 ? ordered[halfcount] : (ordered[halfcount] + ordered[halfcount - 1]) / 2;
            return result;
        }

        public static int Mood(this IEnumerable<int> source)
        {
            var frequencies = source.Frequencies();
            var max = frequencies.Values.Max();
            return frequencies.FirstOrDefault(x => x.Value == max).Key;
        }

        public static Dictionary<int, int> Frequencies(this IEnumerable<int> source)
        {
            return source.GroupBy(n => n).ToDictionary(n => n.Key, n => n.Count());
        }

        public static IEnumerable<U> Rank<T, TKey, U>(
            this IEnumerable<T> source, Func<T, TKey> keySelector, Func<T, int, U> selector)
        {
            if (!source.Any())
            {
                yield break;
            }

            int itemCount = 0;
            T[] ordered = source.OrderBy(keySelector).ToArray();
            TKey previous = keySelector(ordered[0]);
            int rank = 1;
            foreach (T t in ordered)
            {
                itemCount += 1;
                TKey current = keySelector(t);
                if (!current.Equals(previous))
                {
                    rank = itemCount;
                }
                yield return selector(t, rank);
                previous = current;
            }
        }
    }
}
